package Storage;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;

import Object.DSException;
import Object.DSQuery;
import Task.DSTask;
import Task.DSTaskList;

//@author A0103535R
/************************************ Abstract Class DSQueryExecuter ***************************/
public class DSQueryExecuter {

	/**
	 * This function is used to execute query to search tasks
	 * 
	 * @param taskList
	 * 			the taskList provided for searching.
	 * @param query
	 * 			the requirements for searching tasks.
	 * 
	 * @return the task list of tasks meeting the requirements.
	 * 
	 * @throws ParseException
	 */
	public static DSTaskList executeQuery(DSTaskList taskList, DSQuery query)
			throws ParseException {
		taskList = taskList.copy();
		// KeyWords
		if (query.hasKeyWord()) {
			matchKeyWords(taskList, query.getKeyWords());
		}
		// Date
		if (query.hasDateRange()) {
			matchDate(taskList, query.getDateRange(), query.getStartDate(),
					query.getEndDate());
		}
		// DoneStatus
		if (query.getStatus() == DSQuery.STATUS_DONE) {
			matchDone(taskList);
		} else if(query.getStatus() == DSQuery.STATUS_NOT_DONE){
			matchNotDone(taskList);
		}
		// Tags
		if (query.hasTag()) {
			matchTags(taskList, query.getTags());
		}
		return taskList;
	}

	/**
	 * This function is used to search tasks of which task name contain any one of keywords.
	 *
	 * @param taskList
	 * 			the taskList provided for searching.
	 * @param keyWords
	 * 			the list of keyWords for searching
	 */
	private static void matchKeyWords(DSTaskList taskList,
			ArrayList<String> keyWords) {
		for (int i = 0; i < taskList.size(); i++) {
			if (matchKeyWords(taskList.get(i), keyWords)) {
				continue;
			} else {
				taskList.remove(i--);
			}
		}
	}

	/**
	 * This function is used to check whether task contains keyWords 
	 * 
	 * @param task
	 * 			the task to check
	 * @param keyWords
	 * 			the list of keyWords for matching
	 * 
	 * @return the result of whether this task contains any one of keyWords.
	 */
	private static boolean matchKeyWords(DSTask task, ArrayList<String> keyWords) {
		for (int i = 0; i < keyWords.size(); i++) {
			if (task.getTaskName().toLowerCase().contains(keyWords.get(i))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * This function is used to search tasks contain any one of tags.
	 *
	 * @param taskList
	 * 			the taskList provided for searching.
	 * @param tags
	 * 			the list of tags for searching
	 */
	private static void matchTags(DSTaskList taskList, ArrayList<String> tags) {
		for (int i = 0; i < taskList.size(); i++) {
			if (matchTags(taskList.get(i), tags)) {
				continue;
			} else {
				taskList.remove(i--);
			}
		}
	}

	 /**
	 *  This function is used to check whether task contains tags. 
	 * 
	 * @param task
	 * 			the task to check
	 * @param tags
	 * 			the list of tags for matching
	 * 
	 * @return the result of whether this task contains any one of tags.
	 */
	private static boolean matchTags(DSTask task, ArrayList<String> tags) {
		for (int i = 0; i < tags.size(); i++) {
			if (task.containsTag(tags.get(i))) {
				return true;
			}
		}
		return false;
	}

	/**
	 * This function is used to search tasks whose status is marked as done.
	 * 
	 * @param taskList
	 * 			the task list to check
	 */
	private static void matchDone(DSTaskList taskList) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).isDone()) {
				taskList.remove(i--);
			}
		}
	}

	/**
	 * This function is used to search tasks whose status is marked as not done.
	 * 
	 * @param taskList
	 * 			the task list to check
	 */
	private static void matchNotDone(DSTaskList taskList) {
		for (int i = 0; i < taskList.size(); i++) {
			if (taskList.get(i).isDone()) {
				taskList.remove(i--);
			}
		}
	}

 	/**
 	 * This function is used to search tasks that meet the date requirement
 	 * 
 	 * @param taskList
 	 * 			the task list to check
 	 * @param DATE_RANGE
 	 * 			type of date range to check
 	 * @param startDate
 	 * 			start date
 	 * @param endDate
 	 * 			end date
 	 */
	private static void matchDate(DSTaskList taskList, int DATE_RANGE,
			Date startDate, Date endDate) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).hasEndDate()) {
				taskList.remove(i--);
			}
		}

		switch (DATE_RANGE) {
		case DSQuery.DATE_RANGE_BEFORE_START_DATE:
			matchBeforeStartDate(taskList, startDate);
			break;
		case DSQuery.DATE_RANGE_BEFORE_END_DATE:
			matchBeforeEndDate(taskList, endDate);
			break;
		case DSQuery.DATE_RANGE_AFTER_START_DATE:
			matchAfterStartDate(taskList, startDate);
			break;
		case DSQuery.DATE_RANGE_AFTER_END_DATE:
			matchAfterEndDate(taskList, endDate);
			break;
		case DSQuery.DATE_RANGE_DURING_DATE:
			matchAfterStartDate(taskList, startDate);
			matchBeforeEndDate(taskList, endDate);
			break;
		default:
			break;
		}
	}

	/**
	 * This function is used to filter tasks whose start date is before 
	 * the date provided.
	 * 
` 	 * @param taskList
 	 * 			the task list to check
 	 * @param date
 	 * 			date provided for checking
	 */
	private static void matchBeforeStartDate(DSTaskList taskList, Date date) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).getTaskStartDate().before(date)) {
				taskList.remove(i--);
			}
		}
	}

	/**
	 * This function is used to filter tasks whose end date is after
	 * the date provided.
	 *
	 * @param taskList
 	 * 			the task list to check
 	 * @param date
 	 * 			date provided for checking 
	 */
	private static void matchBeforeEndDate(DSTaskList taskList, Date date) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).getTaskEndDate().before(date)) {
				taskList.remove(i--);
			}
		}
	}

	/**
	 * This function is used to filter tasks whose start date is after
	 * the date provided.
	 *
	 * @param taskList
 	 * 			the task list to check
 	 * @param date
 	 * 			date provided for checking 
	 */
	private static void matchAfterStartDate(DSTaskList taskList, Date date) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).getTaskStartDate().after(date)) {
				taskList.remove(i--);
			}
		}
	}

	/**
	 * This function is used to filter tasks whose end date is before
	 * the date provided.
	 * 
	 * @param taskList
 	 * 			the task list to check
 	 * @param date
 	 * 			date provided for checking 
	 */
	private static void matchAfterEndDate(DSTaskList taskList, Date date) {
		for (int i = 0; i < taskList.size(); i++) {
			if (!taskList.get(i).getTaskEndDate().after(date)) {
				taskList.remove(i--);
			}
		}
	}

}